using System;
using System.Globalization;
using System.Net;
using System.Text;
using JetBrains.Annotations;
using SF.Code.Extension.HttpHelper.Downloader;
using SG.Ling.AutoTranslator.NLP;

namespace SG.Ling.AutoTranslator.Translation
{
    public static class BatchTranslatorApi 
    {
        public static TranslationTuple ExtractTranslation(byte[] data, ContentMetaData metaData)
        {
            var html = metaData.GetString(data);

            CultureInfo sourceLang, targetlang;
            html.GetLanguages(out sourceLang, out targetlang);

            string sourceTranslit, targetTranslit;
            html.GetTransliteration(out sourceTranslit, out targetTranslit);

            return new TranslationTuple(sourceLang, html.GetSource(), sourceTranslit,
                                   targetlang, html.GetTranslation(), targetTranslit);
        }

        private static string GetSource(this string html)
        {
            return html.GetTextFromAttributedElement("source", "textarea").Item1;
        }

        private static void GetLanguages(this string html, out CultureInfo source, out CultureInfo target)
        {
            Func<int, Tuple<string, int>> getLang =
                pStartIndex =>
                html.GetTextFromAttributedElement("SELECTED", "option", attrName: "option", attrSep: ' ',
                                                  startIndex: pStartIndex);

            Func<Tuple<string, int>, CultureInfo> getCulture =
                pName =>
                    {
                        CultureInfo culture;
                        var name = pName.Item1;
                        var bracket = name.IndexOf('(');
                        var normalized = (bracket > 0 ? name.Substring(0, bracket) : name).Trim();

                        return Support.Languages.TryGetValue(normalized, out culture)
                                   ? culture
                                   : CultureInfo.InvariantCulture;
                    };

            var sourceElement = getLang(0);
            source = getCulture(sourceElement);
            var targetElement = getLang(sourceElement.Item2);
            target = getCulture(targetElement);
        }

        public static void GetTransliteration(this string html, out string source, out string target)
        {
            Func<string, string> getTranslit =
                pId =>
                    {
                        var src = html.GetTextFromAttributedElement(pId, "div", @throw: false);
                        return src != null && src.Item1 != null ? src.Item1.Trim() : null;
                    };

            source = getTranslit("src-translit");
            target = getTranslit("res-translit");
        }

        public static string GetTranslation(this string html)
        {
            var outer = html.GetTextFromAttributedElement("result_box", "span").Item1;
            return outer.Substring(outer.IndexOf('>') + 1).Trim();
        }

        public static Tuple<string, int> GetTextFromAttributedElement(this string html, string id, string close, string attrName = "id", char attrSep = '=', int startIndex = 0, bool @throw = true)
        {
            var idIndex = html.IndexOf(attrName + attrSep + id, startIndex);
            if (idIndex > 0)
            {
                var start = html.IndexOf('>', idIndex);

                if (start > 0)
                {
                    var end = html.IndexOf("</" + close, start);

                    if (end > 0 && end > start)
                    {
                        var innerHtmlOffset = start + 1;
                        var innerHtml = html.Substring(innerHtmlOffset, end - start - 1);
                        var decoded = WebUtility.HtmlDecode(innerHtml);
                        return Tuple.Create(decoded, innerHtmlOffset);
                    }
                    if (@throw)
                    {
                        throw new ArgumentException(String.Format("Cannot find {0} in {1}", close, html), "close");     
                    }
                }
            }

            if (@throw)
                throw new ArgumentException(String.Format("Cannot find {0} in {1}", id, html), "id");   
            
            return null;
        }


        public static string GetTwoLettercode([CanBeNull] this CultureInfo @this, string @default)
        {
            return @this != null ? @this.TwoLetterISOLanguageName : @default;
        }
    }
}